package concurrent

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"
)

type Counter interface {
	Inc()
	Load() int64
}

type CommonCounter struct {
	counter int64
}

func (c *CommonCounter) Inc() {
	c.counter++
}

func (c *CommonCounter) Load() int64 {
	return c.counter
}

type MutexCounter struct {
	counter int64
	lock    sync.Mutex
}

func (c *MutexCounter) Inc() {
	c.lock.Lock()
	c.counter++
	c.lock.Unlock()
}

func (c *MutexCounter) Load() int64 {
	return c.counter
}

type AtomicCounter struct {
	counter int64
}

func (c *AtomicCounter) Inc() {
	atomic.AddInt64(&c.counter, 1)
}

func (c *AtomicCounter) Load() int64 {
	return atomic.LoadInt64(&c.counter)
}

func test(c Counter) {
	var wait sync.WaitGroup
	start := time.Now()

	for i := 0; i < 1000; i++ {
		wait.Add(1)

		go func() {
			c.Inc()
			wait.Done()
		}()
	}

	wait.Wait()

	end := time.Now()
	fmt.Println(c.Load(), end.Sub(start))
}

func TestAtomic() {
	c1 := CommonCounter{}
	c2 := MutexCounter{}
	c3 := AtomicCounter{}

	test(&c1)
	test(&c2)
	test(&c3)
}

func TestAtomic2() {
	var i int32 = 10
	atomic.StoreInt32(&i, 15)
	fmt.Println(i)

	i1 := atomic.LoadInt32(&i)
	fmt.Println(i1)

	atomic.AddInt32(&i, 1)
	fmt.Println(i)

	atomic.SwapInt32(&i, 32)
	fmt.Println(i)

	atomic.CompareAndSwapInt32(&i, 34, 33)
	fmt.Println(i)
}
